#include "codecaves.h"


void (*cvPrint)(int r,const char *text,...) = (void(*)(int,const char*,...))0x004b3800;
// ******* Codecaving stuff ********
//-----------------------------------------------------------------------------
// Taken from Beginner's Guide to Codecaving tutorial source
// Writes bytes in the current process using an ASM method
VOID WriteBytesASM(DWORD destAddress, LPVOID patch, DWORD numBytes)
{
	// Store old protection of the memory page
	DWORD oldProtect = 0;

	// Store the source address
	DWORD srcAddress = PtrToUlong(patch);

	// Make sure page is writeable
	VirtualProtect((void*)(destAddress), numBytes, PAGE_EXECUTE_READWRITE, &oldProtect);

	// Do the patch (oldschool style to avoid memcpy)
	__asm
	{
		nop						// Filler
		nop						// Filler
		nop						// Filler

		mov esi, srcAddress		// Save the address
		mov edi, destAddress	// Save the destination address
		mov ecx, numBytes		// Save the size of the patch
Start:
		cmp ecx, 0				// Are we done yet?
		jz Exit					// If so, go to end of function

		mov al, [esi]			// Move the byte at the patch into AL
		mov [edi], al			// Move AL into the destination byte
		dec ecx					// 1 less byte to patch
		inc esi					// Next source byte
		inc edi					// Next destination byte
		jmp Start				// Repeat the process
Exit:
		nop						// Filler
		nop						// Filler
		nop						// Filler
	}

	// Restore old page protection
	VirtualProtect((void*)(destAddress), numBytes, oldProtect, &oldProtect);
}

void WriteBytes(void* address, void* bytes, int size)
{
	unsigned long oldProt = 0;

	VirtualProtect(address, size, PAGE_EXECUTE_READWRITE, &oldProt); // Gain access to memory
	memcpy(address, bytes, size); // Write to the memory
	VirtualProtect(address, size, oldProt, &oldProt); // Remove access to memory
}
//-----------------------------------------------------------------------------
// Taken from Beginner's Guide to Codecaving tutorial source
// Codecave function
VOID Codecave(DWORD destAddress, VOID (*func)(VOID), BYTE nopCount)
{
	// Calculate the code cave for chat interception
	DWORD offset = (PtrToUlong(func) - destAddress) - 5;

	// Buffer of NOPs, static since we limit to 'UCHAR_MAX' NOPs
	BYTE nopPatch[0xFF] = {0};

	// Construct the patch to the function call
	BYTE patch[5] = {0xE8, 0x00, 0x00, 0x00, 0x00};
	memcpy(patch + 1, &offset, sizeof(DWORD));
	WriteBytesASM(destAddress, patch, 5);

	// We are done if we do not have NOPs
	if(nopCount == 0)
		return;

	// Fill it with nops
	memset(nopPatch, 0x90, nopCount);

	// Make the patch now
	WriteBytesASM(destAddress + 5, nopPatch, nopCount);
}

DWORD SAPPCHANDLER;
DWORD RetAddr;
DWORD CmdStrAddr;
DWORD CmdSuccess;


__declspec(naked) void InterceptSAPP_CHandler_Cmd(void)
{
	__asm
	{
	pop RetAddr
	MOV CmdStrAddr, EDI
	MOV SAPPCHANDLER, EAX
	PUSHAD
	PUSHFD
	}
	// Invoke our C++ function now
	CmdSuccess = CustmCmd((char*)CmdStrAddr);

	__asm
	{ // restore stack/registers
		POPFD
		POPAD

		MOV EAX, CmdSuccess
	}
	if(!CmdSuccess && SAPPCHANDLER)
	{ 
		__asm
		{// Call SAPP if the command didn't match already.
		MOV EAX, SAPPCHANDLER
		PUSH EDI
		CALL EAX
		}
	}
	__asm
	{
	TEST EAX, EAX
	// The last thing we must do in our codecave is push
	// the return address back onto the stack and then RET back
	push RetAddr
	ret
	}
}
/*
sapp's Call to codecave, intercepts Console and Rcon commands

004B3A76   |.  51                   PUSH ECX                                   ;  KERNELBA.757A189F
004B3A77   |.  68 E4505600          PUSH halocede.005650E4                     ;  ASCII "Requested function \"%s\" cannot be executed now."
004B3A7C   |.  53                   PUSH EBX
004B3A7D   |.  E8 7EFDFFFF          CALL halocede.004B3800
004B3A82   |.  83C4 0C              ADD ESP,0C
004B3A85   |.  5E                   POP ESI                                    ;  ntdll.77564C1C
004B3A86   |.  8AC3                 MOV AL,BL



Codecave @: 004B3A76, only catches cmds that havent matched yet
command string is in EAX and in EDI

*/
__declspec(naked) void InterceptNormalCmd(void)
{
	__asm
	{
	pop RetAddr
	MOV CmdStrAddr, EDI
	PUSHAD
	PUSHFD
	}
	// Invoke our C++ function now
	CmdSuccess = CustmCmd((char*)CmdStrAddr);

	__asm
	{ // restore stack/registers
		POPFD
		POPAD

		MOV EAX, CmdSuccess
	}
	if(!CmdSuccess)
	{ // display the 'function not found' msg
		cvPrint(0, (char*)0x005650E4, CmdStrAddr);
	}
		// and do additional stuff
	__asm{
	ADD ESP,0x0C
	POP ESI
	MOV AL,BL
	push RetAddr
	ret
	}
}

__declspec(naked) void InterceptCmd(void)
{
	__asm
	{
	pop RetAddr
	MOV CmdStrAddr, EDI
	PUSHAD
	PUSHFD
	}
	// Invoke our C++ function now
	CmdSuccess = CustmCmd((char*)CmdStrAddr);

	__asm
	{ // restore stack/registers
		POPFD
		POPAD

		MOV EAX, CmdSuccess
		push RetAddr
		ret
	}
}
void CodecaveCheckCmd()
{
	// Old Cmds:
	//char* oldcmd = {0x57, 0xFF, 0xD0, 0x85, 0xC0};
	// New Cmds:
	// 2 ways: WITH the SAPP (0x0054C4BA), or with the original (0x004B3A76).
	// Update: Overwrite the Get_SAPP_CHandler completely (34 bytes)
	/*
	0054C49B   |> \68 A3C35400       PUSH halocede.0054C3A3                     ; /pModule = "sapp.dll"
	0054C4A0   |.  FF15 7CD25400     CALL DWORD PTR DS:[<&KERNEL32.GetModuleHan>; \GetModuleHandleA
	0054C4A6   |.  68 B8C35400       PUSH halocede.0054C3B8                     ; /ProcNameOrOrdinal = "CHandler"
	0054C4AB   |.  50                PUSH EAX                                   ; |hModule = 77971162
	0054C4AC   |.  FF15 98D05400     CALL DWORD PTR DS:[<&KERNEL32.GetProcAddre>; \GetProcAddress
	0054C4B2   |.  85C0              TEST EAX,EAX                               ;  ?SAPP.CHANDLER
	0054C4B4   |.  74 07             JE SHORT halocede.0054C4BD
	0054C4B6   |.  8D4C24 0C         LEA ECX,DWORD PTR SS:[ESP+C]
	0054C4BA   |.  57                PUSH EDI
	0054C4BB   |.  FFD0              CALL EAX                                   ;  SAPP.CHANDLER
	//0054C4BD   |> \85C0              TEST EAX,EAX                               ;  CHANDLER_RETURN_STATE

	*/

	// (original: 68 A3 C3 54 00 FF 15 7C D2 54 00 68 B8 C3 54 00 50 FF 15 98 D0 54 00 85 C0 74 07 8D 4C 24 0C 57 FF D0)
	// eventually have that restored on DLL_UNLOAD
	//DWORD addr = 0x0054C4BA;
	//Codecave(addr, &InterceptCmd, 0);

	DWORD addr = 0x0054C49B;
	Codecave(addr, &InterceptCmd, 29);

}

// Taken by reversing the SAPP.
// Addy of Chat codecave: 0x0049F811
// SAPP's codecave: JMP 0x10008B20, <nop>
// that JMP is apparently not really constant.
// bytes: {0xE9, 0x0A, 0x93, 0xB6, 0x0F, 0x90};
// Then, the Chat char* is apparently on the stack, at addr 0x0012C908. OR, if i wanna be pro and stuff, go off of the stack pointer: [esp + 0x48]
// And there's a pointer to the AlterPlayer2 struct that sent the chat in ESI register

DWORD GlobalReturn = 0x0049FA91;
DWORD TeamReturn = 0x0049FA02;
DWORD ChatAddr = NULL;
DWORD PlayerStructAddr = NULL;
bool BlockChat = false;
__declspec(naked) void hkGlobalChat(void* lpVoid)
{
    __asm
	{
		LEA ECX,DWORD PTR SS:[ESP+38h]
		PUSH ECX
		LEA ESI,DWORD PTR DS:[EAX+4h]
		PUSH ESI
		MOV ChatAddr, ECX
		MOV PlayerStructAddr, ESI
		PUSHAD
		PUSHFD
	}

	BlockChat = ChatIntercept((wchar_t*)ChatAddr, (Static_Player*)(PlayerStructAddr - 4));

	__asm 
	{
		POPFD
		POPAD
		JMP GlobalReturn
	}
}

__declspec(naked) void hkTeamChat(void* lpVoid)
{
    __asm
	{
		LEA ECX,DWORD PTR SS:[ESP+38h]
		PUSH ECX
		LEA ESI,DWORD PTR DS:[EAX+4h]
		PUSH ESI
		MOV ChatAddr, ECX
		MOV PlayerStructAddr, ESI
		PUSHAD
		PUSHFD
	}

	CmdSuccess = ChatIntercept((wchar_t*)ChatAddr, (Static_Player*)(PlayerStructAddr - 4));

	__asm 
	{
		POPFD
		POPAD
		JMP TeamReturn
	}
}

void CodecaveChatCmd()
{
	DetourFunction((PBYTE)0x0049FA88,(PBYTE)&hkGlobalChat); // Global
	DetourFunction((PBYTE)0x0049F9F9,(PBYTE)&hkTeamChat); // Team
}



// DEATH/killed/suicide events:
/*
suicide, then death inc.
//THESE are HALOCE.exe addys.
// jne is to check if it was a suicide.
0046A731 - 75 07                      - jne 0046a73a
0046A733 - 66 ff 80 b0 00 00 00       - inc word ptr [eax+000000b0]
0046A73A - 66 ff 80 ae 00 00 00       - inc word ptr [eax+000000ae]
0046A741 - 33 ed                      - xor ebp,ebp



Killed:
(much later on)
0046AB5A - 66 ff 82 9c 00 00 00       - inc word ptr [edx+0000009c]
0046AB61 - 0f bf 82 9a 00 00 00       - movsx eax,word ptr [edx+0000009a]

*/

DWORD DeathAddr   = 0x0046A73A;
DWORD DeathReturn = 0x0046A741;
DWORD PlayerAddr;
__declspec(naked) void hkDeath(void* lpVoid)
{
	__asm
	{
		PUSHAD
		PUSHFD
		INC word ptr [eax+0xAE]
		MOV PlayerAddr, EAX
	}

	//Manager()->OnDeath((Static_Player*)PlayerAddr);

	__asm 
	{
		POPFD
		POPAD
		JMP DeathReturn
	}
}

__declspec(naked) void hkSuicide(void* lpVoid)
{
	__asm
	{
		PUSHAD
		PUSHFD
		MOV PlayerAddr, EAX
		INC word ptr [eax+0xB0]
	}

	//OnSuicide((Static_Player*)PlayerAddr);

	__asm 
	{
		POPFD
		POPAD
		JMP DeathAddr // goes back to the Death part, which Will call OnDeath too.
	}
}

DWORD KilledReturn = 0x0046AB61;
__declspec(naked) void hkKilled(void* lpVoid)
{
	__asm
	{
		PUSHAD
		PUSHFD
		MOV PlayerAddr, EAX
		INC word ptr [eax+0x9C]
	}

	Manager()->OnDeath((Static_Player*)PlayerAddr);
	//OnKilled((Static_Player*)PlayerAddr);

	__asm 
	{
		POPFD
		POPAD
		JMP KilledReturn
	}
}


void CodecavePlayerScoreEvents()
{
	DetourFunction((PBYTE)DeathAddr,(PBYTE)&hkDeath);
	
	DetourFunction((PBYTE)0x0046A733,(PBYTE)&hkSuicide);
	DetourFunction((PBYTE)0x0046AB5A,(PBYTE)&hkKilled);
}